 /* Copyright (C)
  * 2024 ZKLX Optoelectronic Technology Co.LTD
  * All rights reserved.
  */
 /**
  * @file jcvp.cpp
  * @brief JCVP - json ctrl and video play
  * @author xuke
  * @version 1.0
  * @date 2024-10-14
  */

#include <iostream>
#include <sstream>
#include <thread>
#include <xcrow_all.h>
#include <opencv2/opencv.hpp>
#include "jcvp.h"

/*--------------------------------extern--------------------------------------------*/

/*--------------------------------globals-------------------------------------------*/

/*--------------------------------locals--------------------------------------------*/
static std::string l_root_dir;

/*--------------------------------funcs---------------------------------------------*/
/**
 * @brief URL解码接口
 * @param input
 * @return 0
 */
std::string misc_url_decode(const std::string& input)
{
	std::string output;
	output.reserve(input.size());

	for (size_t i = 0; i < input.size(); ++i) {
		if (input[i] == '%') {
			if (i + 3 <= input.size()) {
				int value = 0;
				std::istringstream is(input.substr(i + 1, 2));
				if (is >> std::hex >> value) {
					output += static_cast<char>(value);
					i += 2;
					continue;
				}
			}
			output += "%"; // Invalid encoding, keep original
		} else if (input[i] == '+') {
			output += ' '; // '+' decodes to space
		} else {
			output += input[i];
		}
	}
	return output;
}


/**
 * @brief video播放线程
 * @param conn
 */
static void video_player_task(JCVP_T *jcvp_obj)
{
	while (jcvp_obj->_conn) {
		//show
		jcvp_obj->vp_show();
		usleep((1000/jcvp_obj->get_vp_fps())*1000);
	}
	printf("player thread exit!\n");
}

/**
 * @brief web_server
 * @param jcvp_obj
 * @return 0
 */
static int run_web_server(JCVP_T *jcvp_obj)
{
	crow::mustache::set_global_base(jcvp_obj->get_web_root());
	crow::SimpleApp app;

	//默认页
	CROW_ROUTE(app, "/") ([]() {
		auto page = crow::mustache::load("index.html");
		return page.render();
	});

	// 处理HTML
	CROW_ROUTE(app, "/html/<string>") ([](const crow::request& req, std::string htmlFile) {
		std::string htmlPath = l_root_dir + "/html/" + htmlFile; // HTML路径
		std::ifstream t(htmlPath);
		std::string htmlCode((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());

		crow::response res;
		res.set_header("Content-Type", "text/html");
		res.body = htmlCode;
		return res;
	});

	// 处理CSS
	CROW_ROUTE(app, "/css/<string>") ([](const crow::request& req, std::string cssFile) {
		std::string cssPath = l_root_dir + "/css/" + cssFile; // CSS路径
		std::ifstream t(cssPath);
		std::string cssCode((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());

		crow::response res;
		res.set_header("Content-Type", "text/css");
		res.body = cssCode;
		return res;
	});

	// 处理JS
	CROW_ROUTE(app, "/js/<string>") ([](const crow::request& req, std::string jsFile) {
		std::string jsPath = l_root_dir + "/js/" + jsFile; // JS路径
		std::ifstream t(jsPath);
		std::string jsCode((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());

		crow::response res;
		res.set_header("Content-Type", "application/javascript");
		res.body = jsCode;
		return res;
	});

	// 处理图片请求的路由
	CROW_ROUTE(app, "/img/<string>") ([](const crow::request& req, std::string imgFile) {
		// 打开图片文件
		std::string imgPath = l_root_dir + "/img/" + imgFile; // 图片路径
		std::ifstream file(imgPath, std::ios::binary);
		if (!file) {
			return crow::response(404); // 如果文件不存在，返回404错误
		}

		// 创建响应对象
		crow::response res;
		res.set_header("Content-Type", "image/jpng"); // 设置内容类型为图片
		res.body = std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()); // 读取文件内容到响应体
		return res;
	});

	//websocket
	CROW_WEBSOCKET_ROUTE(app, "/ws/video")
		.onopen([&](crow::websocket::connection& conn) {
			// WebSocket连接已建立
			printf("onopen\n");
			if (!jcvp_obj->_conn) {
				jcvp_obj->_conn = (void*)&conn;

				//player thread
				std::thread vp_player_thread(video_player_task, jcvp_obj);
				vp_player_thread.detach();

				printf("vp player task start\n");
			} else {
				printf("capture task is already running!\n");
			}
		})
		.onclose([&](crow::websocket::connection& conn, const std::string& reason) {
			// WebSocket连接已关闭
			printf("onclose\n");
			jcvp_obj->_conn = NULL;
		})
		.onmessage([&](crow::websocket::connection& conn, const std::string& data, bool is_binary){
			// 处理从前端接收的WebSocket消息, 在这里处理音视频数据的传输
			if (is_binary) { //二进制数处理
				printf("onmessage recv binary data.\n");
				//conn.send_binary(data); //echo
			} else {
				//printf("onmessage: %s\n", data.c_str());
				//conn.send_text(data); //echo
				jcvp_obj->_jc_str = data;
				jcvp_obj->_is_new_jc_str = 1;
			}
		});

	//其他route
	CROW_ROUTE(app, "/<string>") ([](const crow::request& req, std::string args) {
		 printf("unsupport route for: [%s]\n", args.c_str());
		 return crow::response(404);
	});

	app.loglevel(crow::LogLevel::Warning);
	//app.port(18080).multithreaded().run();
	app.port(jcvp_obj->get_port()).run();
	return 0;
}

JCVP_T::JCVP_T(std::string web_root, int port, int vp_fps, float img_scale)
{
	_web_root = web_root;
	_port = port;
	_vp_fps = vp_fps;
	_img_scaling = img_scale;

	_new_frame = cv::Mat(cv::Size(640, 512), CV_8UC3, cv::Scalar(0));

	l_root_dir = _web_root;
}

JCVP_T::JCVP_T()
{
	_new_frame = cv::Mat(cv::Size(640, 512), CV_8UC3, cv::Scalar(0));

	l_root_dir = _web_root;
}

JCVP_T::~JCVP_T()
{
}

int JCVP_T::webs_run()
{
	std::thread web_server_thread(run_web_server, this);
	web_server_thread.detach();
	return 0;
}

int JCVP_T::frame_update(cv::Mat frame)
{
	frame.copyTo(_new_frame);
	return 0;
}

void JCVP_T::vp_show()
{
	cv::Mat scale_frame;
	std::vector<uchar> buf;

	//new frame 处理
	try {
		cv::resize(_new_frame, scale_frame, cv::Size(_new_frame.cols/_img_scaling, _new_frame.rows/_img_scaling));
	} catch(...) {
		return;
	}

	// 编码为JPEG格式
	cv::imencode(".jpg", scale_frame, buf);

	// 发送JPEG数据给前端
	crow::websocket::connection *ctx_conn = (crow::websocket::connection*)_conn;
	if (ctx_conn) {
		std::string bin_string(buf.size(), 0);
		std::copy(buf.begin(), buf.end(), bin_string.begin());
		if (ctx_conn != nullptr) {
			ctx_conn->send_binary(bin_string);
		}
	}
}

void JCVP_T::jc_send(std::string data_str)
{
	crow::websocket::connection *ctx_conn = (crow::websocket::connection*)_conn;
	if (ctx_conn != nullptr) {
		ctx_conn->send_text(data_str);
	}
}

std::string JCVP_T::jc_recv()
{
	while (!_is_new_jc_str) {
		usleep(5*1000);
	}
	_is_new_jc_str = 0;
	return _jc_str;
}

std::string JCVP_T::get_web_root()
{
	return _web_root;
}
float JCVP_T::get_img_scaling()
{
	return _img_scaling;
}
int JCVP_T::get_vp_fps()
{
	return _vp_fps;
}
int JCVP_T::get_port()
{
	return _port;
}

